home *** CD-ROM | disk | FTP | other *** search
- #ifndef lint
- static char RCSid[] =
- "$Id: qterm.c,v 6.14 1993/06/14 22:24:04 mcooper Exp mcooper $";
-
- static char copyright[] =
- "@(#) Copyright (c) 1990-1993 Michael A. Cooper.\n\
- All rights reserved.\n";
- #endif
-
- /*
- * Copyright (c) 1990-1993 Michael A. Cooper.
- * This software may be freely distributed provided it is not sold for
- * profit and the author is credited appropriately.
- */
-
- /*
- * qterm - Query Terminal
- *
- * qterm is used to query a terminal to determine the name of the terminal.
- * This is done by sending a fairly universal string "\33Z" to the terminal,
- * reading in a response, and comparing it against a master table of responses
- * and names. The "name" printed to standard output should be one found in
- * the termcap(5) database.
- *
- * Putting a line in your ".login" file such as:
- *
- * setenv TERM `qterm`
- *
- * or the following lines in your ".profile" file:
- *
- * TERM=`qterm`
- * export TERM
- *
- * will set your terminal type automagically.
- *
- * If you add a terminal to the master table, please also send me a copy
- * so that I may put it into my version.
- *
- * Michael A. Cooper
- * mcooper@usc.edu
- */
-
- #include "config.h"
-
- #include <stdio.h>
- #include <ctype.h>
- #include <signal.h>
- #include <sys/ioctl.h>
- #include <setjmp.h>
- #include "qterm.h"
- #include "version.h"
- #include "options.h"
-
- int Found = FALSE;
- int ModesSet = FALSE;
- jmp_buf JmpEnv;
- char *ProgName = NULL;
- char *TermFile = NULL;
- char **CommonSeqs = NULL;
- char *QueryStr = NULL;
- struct termtable *TermTable = NULL;
-
- int Debug = FALSE; /* Debug mode */
- int PrVersion = FALSE; /* Print version */
- int UseAltStr = FALSE; /* Alternate string */
- int ToWait = FALSE; /* Time out wait flag */
- int AlwaysSend = FALSE; /* Intense query mode */
- int LongName = FALSE; /* Print long terminal name */
- int SentChars = FALSE; /* Print strings sent from the terminal */
- int WatchChars = FALSE; /* Watch strings as they are sent and recv. */
- int Quiet = FALSE; /* Quiet mode */
- int DoUsrTabFile = FALSE; /* Use user's own .qtermtab file */
- int DoSysTabFile = TRUE; /* Use the system's qterm tab file */
- int ShowReal = FALSE; /* Show real terminal name */
- int TimeOut = TIMEOUT; /* Wait (timeout) interval */
- int TryCommon = FALSE; /* Try sending common answerback strings */
-
- /*
- * Old options should not be visable in help and usage messages.
- */
- #ifdef OPT_COMPAT
- #define OLD_NoArg NoArg|ArgHidden
- #define OLD_SepArg SepArg|StickyArg|ArgHidden
- #define fFLAG "-f"
- #define FFLAG "-F"
- #endif
-
- /*
- * Command line options table.
- */
- OptionDescRec opts[] = {
- #ifdef OPT_COMPAT
- {"-a", OLD_NoArg, OptInt, (caddr_t) &UseAltStr, "1",
- (char *)NULL, "Use alternate query string"},
- {"-s", OLD_NoArg, OptInt, (caddr_t) &SentChars, "1",
- (char *)NULL, "Display the characters the terminal sent"},
- {"-t", ArgHidden|OLD_NoArg, OptInt, (caddr_t) &SentChars, "1",
- (char *)NULL, "Display the characters the terminal sent"},
- {"-I", OLD_NoArg, OptInt, (caddr_t) &AlwaysSend, "1",
- (char *)NULL, "Always send the terminal query string"},
- {"-T", OLD_NoArg, OptInt, (caddr_t) &ToWait, "1",
- (char *)NULL, "Enable time out wait"},
- {"-S", OLD_NoArg, OptInt, (caddr_t) &WatchChars, "1",
- (char *)NULL, "Print strings as they are sent and received"},
- {"-q", OLD_NoArg, OptInt, (caddr_t) &Quiet, "1",
- (char *)NULL, "Enable quite mode"},
- {"-f", OLD_SepArg, OptStr, (caddr_t) &TermFile, fFLAG,
- "<tabfile>", "Try <tabfile>, then ~/.qtermtab, then system tabfile"},
- {"-F", OLD_SepArg, OptStr, (caddr_t) &TermFile, FFLAG,
- "<tabfile>", "Try <tabfile>, then ~/.qtermtab"},
- {"-l", OLD_NoArg, OptInt, (caddr_t) &LongName, "1",
- (char *)NULL, "Output only the long (verbose) terminal name"},
- {"-d", OLD_NoArg, OptInt, (caddr_t) &Debug, "1",
- (char *)NULL, "Enable debug mode"},
- {"-w", OLD_SepArg, OptInt, (caddr_t) &TimeOut, __ NULL,
- "<interval>", "Wait (timeout) period (in seconds)"},
- #endif /*OPT_COMPAT*/
- {"+alt", NoArg, OptBool, (caddr_t) &UseAltStr, "1",
- (char *)NULL, "Use alternate query string"},
- {"-alt", NoArg, OptBool, (caddr_t) &UseAltStr, "0",
- (char *)NULL, "Don't use alternate query string"},
- {"+always", NoArg, OptBool, (caddr_t) &AlwaysSend, "1",
- (char *)NULL, "Always send the terminal query string"},
- {"-always", NoArg, OptBool, (caddr_t) &AlwaysSend, "0",
- (char *)NULL, "Don't always send the terminal query string"},
- {"-file", SepArg, OptStr, (caddr_t) &TermFile, __ NULL,
- "<tabfile>", "Use <tabfile> to query terminal"},
- {"+longname",NoArg, OptBool, (caddr_t) &LongName, "1",
- (char *)NULL, "Output only the long (verbose) terminal name"},
- {"-longname",NoArg, OptBool, (caddr_t) &LongName, "0",
- (char *)NULL, "Don't output the long (verbose) terminal name"},
- {"-querystr",SepArg, OptStr, (caddr_t) &QueryStr, __ NULL,
- "<str>", "Query string to use"},
- {"+quiet", NoArg, OptBool, (caddr_t) &Quiet, "1",
- (char *)NULL, "Enable quiet mode"},
- {"-quiet", NoArg, OptBool, (caddr_t) &Quiet, "0",
- (char *)NULL, "Disable quiet mode"},
- {"+real", NoArg, OptBool, (caddr_t) &ShowReal, "1",
- (char *)NULL, "Determine real name of terminal"},
- {"-real", NoArg, OptBool, (caddr_t) &ShowReal, "0",
- (char *)NULL, "Determine generic name of terminal"},
- {"+sent", NoArg, OptBool, (caddr_t) &SentChars, "1",
- (char *)NULL, "Display the characters the terminal sent"},
- {"-sent", NoArg, OptBool, (caddr_t) &SentChars, "0",
- (char *)NULL, "Don't display the characters the terminal sent"},
- {"+timeout",NoArg, OptBool, (caddr_t) &ToWait, "1",
- (char *)NULL, "Enable time out wait"},
- {"-timeout",NoArg, OptBool, (caddr_t) &ToWait, "0",
- (char *)NULL, "Disable time out wait"},
- {"+trycommon",NoArg, OptBool, (caddr_t) &TryCommon, "1",
- (char *)NULL, "Try sending common answerback sequences"},
- {"-trycommon",NoArg, OptBool, (caddr_t) &TryCommon, "0",
- (char *)NULL, "Disable sending common answerbook sequences"},
- {"+usrtab", NoArg, OptBool, (caddr_t) &DoUsrTabFile, "1",
- (char *)NULL, "Enable using ~/.qtermtab"},
- {"-usrtab", NoArg, OptBool, (caddr_t) &DoUsrTabFile, "0",
- (char *)NULL, "Disable using ~/.qtermtab"},
- {"-wait", SepArg, OptInt, (caddr_t) &TimeOut, __ NULL,
- "<interval>", "Wait (timeout) period (in seconds)"},
- {"+watch", NoArg, OptBool, (caddr_t) &WatchChars, "1",
- (char *)NULL, "Watch the characters sent and recieved"},
- {"-watch", NoArg, OptBool, (caddr_t) &WatchChars, "0",
- (char *)NULL, "Don't watch the characters sent and recieved"},
- {"+systab", NoArg, OptBool, (caddr_t) &DoSysTabFile, "1",
- (char *)NULL, "Enable using system qtermtab file"},
- {"-systab", NoArg, OptBool, (caddr_t) &DoSysTabFile, "0",
- (char *)NULL, "Disable using system qtermtab file"},
- {"-version", NoArg, OptBool,(caddr_t) &PrVersion, "1",
- (char *)NULL, "Print version information"},
- {"-debug", ArgHidden|NoArg, OptInt, (caddr_t) &Debug, "1",
- (char *)NULL, "Enable debug mode"},
- };
-
- /*
- * Reset terminal and exit with status s.
- */
- void Done(s)
- int s;
- {
- ReSetModes();
- exit(s);
- }
-
- /*
- * Catch kill signals and cleanup.
- */
- void Catch(signo)
- int signo;
- {
- dprintf("[ Caught signal %d ]\n", signo);
- Done(2);
- /*NOTREACHED*/
- }
-
- /*
- * Go here when alarm goes off
- */
- void WakeUp()
- {
- dprintf("[ WakeUp called ]\n");
- longjmp(JmpEnv, 1);
- dprintf("[ longjmp failed! ]\n");
- }
-
- /*
- * Config() - Perform configuration operations.
- */
- Config(argc, argv)
- int argc;
- char **argv;
- {
- ProgName = argv[0];
-
- /*
- * Parse command line args
- */
- if (ParseOptions(opts, Num_Opts(opts), argc, argv) < 0)
- Done(1);
- /*NOTREACHED*/
-
- /*
- * Check results of command line parsing and perform any
- * needed post processing.
- */
-
- if (LongName)
- Quiet = TRUE;
-
- if (TimeOut == 0) {
- Error("Alarm (wait) time must be greater than 0.");
- Done(1);
- /*NOTREACHED*/
- }
-
- #if defined(OPT_COMPAT)
- /*
- * Kludgy stuff to be backwards compatable for command line options.
- */
- if (TermFile) {
- if (strcmp(TermFile, fFLAG) == 0) {
- DoUsrTabFile = TRUE;
- DoSysTabFile = TRUE;
- TermFile = NULL;
- } else if (strcmp(TermFile, FFLAG) == 0) {
- DoUsrTabFile = TRUE;
- DoSysTabFile = FALSE;
- TermFile = NULL;
- }
- }
- #endif /*OPT_COMPAT*/
-
- dprintf("[ %s debug mode enabled ]\n\n", ProgName);
- }
-
- /*
- * Set signal catches and terminal modes
- */
- SetModes()
- {
- if (!isatty(0)) {
- Error("This program can only be run on a tty device.");
- Done(0);
- /*NOTREACHED*/
- }
-
- /*
- * Set output buffers
- */
- setbuf(stdout, (char *)0);
- if (Debug)
- setbuf(stderr, (char *)0);
-
- /*
- * Cleanup terminal modes & such if we are killed
- */
- SetupSignals(Catch);
-
- /*
- * Set terminal modes
- */
- if (SetTtyModes() == 0)
- ModesSet = TRUE;
- }
-
- /*
- * Reset terminal modes
- */
- ReSetModes()
- {
- dprintf("[ ReSetModes called - %s ]\n",
- (ModesSet) ? "ModesSet" : "Modes not set");
- if (ModesSet)
- UnSetTtyModes();
- }
-
- /*
- * Print a message since we didn't recognize this terminal.
- */
- void NotRecognized()
- {
- char *envterm;
-
- if ((envterm = getenv("TERM")) == NULL)
- envterm = "dumb";
-
- if (!Quiet)
- (void) fprintf(stderr,
- "Terminal NOT recognized - defaults to \"%s\".\r\n",
- envterm);
-
- puts(envterm);
- }
-
- /*
- * Decode - print str in a readable fashion
- */
- char *Decode(str)
- char *str;
- {
- register int len;
- static char buf[BUFSIZ];
- char tmp[10];
-
- if (!str)
- return("(null)");
-
- buf[0] = (char) NULL;
- while (*str) {
- if (*str == ESC)
- (void) strcat(buf, "<esc> ");
- else if ((*str <= 33) || (*str >= 127)) {
- (void) sprintf(tmp,"\\%#o ", (unsigned) *str);
- (void) strcat(buf, tmp);
- } else {
- (void) sprintf(tmp, "%c ", *str);
- (void) strcat(buf, tmp);
- }
- ++str;
- }
-
- len = strlen(buf);
- if (len && buf[len - 1] == ' ')
- buf[len - 1] = (char) NULL;
-
- return(buf);
- }
-
- /*
- * Print info about terminal structure termtable.
- */
- void PrInfo(termtable, recvstr)
- struct termtable *termtable;
- char *recvstr;
- {
- int len = 0;
- char *termname = NULL;
-
- if (Debug || SentChars) {
- len = strlen(recvstr);
- (void) fprintf(stderr, "%s received %d character%s:",
- ProgName, len, (len == 1) ? "" : "s");
- (void) fprintf(stderr, " %s\r\n", Decode(recvstr));
- }
-
- if (ShowReal && (strcmp(termtable->qt_ntermname, "-") != 0))
- termname = termtable->qt_ntermname;
- else
- termname = termtable->qt_termname;
-
- if (!Quiet) {
- (void) fprintf(stderr, "Terminal recognized as %s", termname);
- if (termtable->qt_fullname && termtable->qt_fullname[0])
- (void) fprintf(stderr, " (%s)\r\n", termtable->qt_fullname);
- else
- (void) fprintf(stderr, "\r\n");
- }
-
- if (LongName) {
- if (termtable->qt_fullname && termtable->qt_fullname[0])
- (void) printf("%s\n", termtable->qt_fullname);
- else
- Error("No full terminal name for %s.", termname);
- } else
- (void) printf("%s\n", termname);
- }
-
- /*
- * FindTermTab - actually compare what we received against the table.
- */
- struct termtable *FindTermTab(str, primett)
- char *str;
- struct termtable *primett;
- {
- register struct termtable *tp;
- static char buff[BUFSIZ];
-
- dprintf(" FindTermTab: %s \tPrime = '%s'\n",
- (str && str[0]) ? Decode(str) : "nothing",
- (primett) ? primett->qt_ntermname : "<none>");
-
- AlarmOff();
-
- if (!str || !*str)
- return((struct termtable *)NULL);
-
- for (tp = TermTable; tp != NULL; tp = tp->nxt) {
- /*
- * If a primary termtab entry was given, then we
- * want to only check secondary entries whose primary
- * terminal name matches the primary's "next" name.
- */
- if (primett && (tp->qt_etype != ET_SECONDARY ||
- strcmp(primett->qt_ntermname, tp->qt_termname)))
- continue;
-
- dprintf(" with %s \t('%s' '%s')",
- Decode(tp->qt_recvstr),
- A(tp->qt_termname), A(tp->qt_ntermname));
-
- (void) sprintf(buff, "^%s$", tp->qt_recvstr);
-
- if (strcmp(tp->qt_recvstr, str) == 0 || RegExMatch(buff, str) > 0) {
- Found = TRUE;
- dprintf("\tMATCHED\n");
- return(tp);
- } else
- dprintf("\tno match\n");
- }
- Found = FALSE;
-
- return((struct termtable *)NULL);
- }
-
- /*
- * GetChar - read in a character at a time.
- */
- char GetChar()
- {
- char c;
-
- (void) read(fileno(stdin), &c, 1);
-
- return(c & CHAR_MASK);
- }
-
- /*
- * Listen for a response.
- */
- char *InputListen(termtab)
- struct termtable *termtab;
- {
- register int i = 0;
- register int len = 0;
- register char c;
- char end;
- static char recvbuff[RECVSIZE];
-
- AlarmOff();
- recvbuff[0] = (char) NULL;
-
- if (termtab && termtab->qt_recvstr)
- len = strlen(termtab->qt_recvstr);
-
- if (len)
- end = termtab->qt_recvstr[len - 1];
- else
- end = 'c'; /* Fairly standard ANSI default */
-
- if (termtab)
- dprintf("\nlisten for %s\t [ len = %d, end = `%c' ]\n",
- Decode(termtab->qt_recvstr), len, end);
- else
- dprintf("\n[ listen for response ]\n");
-
- /*
- * Read in remaining response. Loop until ending character
- * is received or until alarm goes off. If ToWait is set,
- * then let alarm go off.
- */
- for (i = 0, c = -1; (!ToWait && (c != end)) || ToWait; ) {
- if (setjmp(JmpEnv)) {
- /*
- * Alarm went off
- */
- if (Found)
- Done(0);
- /*NOTREACHED*/
- (void) fflush(stdin);
- (void) fflush(stdout);
- (void) fflush(stderr);
- recvbuff[i] = (char) NULL;
- return((recvbuff[0]) ? recvbuff : (char *)NULL);
- }
-
- AlarmOn(TimeOut, WakeUp);
- c = GetChar();
- AlarmOff();
-
- recvbuff[i++] = c;
- }
- recvbuff[i] = (char) NULL;
-
- dprintf("[ listen done. read %d chars ]\n\n", i);
-
- return((recvbuff[0]) ? recvbuff : (char *)NULL);
- }
-
- /*
- * Process entries in the termtable.
- */
- void ProcTable(termtab, primett, etype)
- struct termtable *termtab;
- struct termtable *primett;
- int etype;
- {
- static struct termtable *lastt;
- register struct termtable *tptr;
- register struct termtable *tp;
- char *recvstr;
- char *querystr = NULL;
- static char *lastquerystr = NULL;
-
- dprintf("\n[ Processing entries: Prime = '%s' etype = %d ] \n",
- (primett) ? primett->qt_ntermname : "<none>", etype);
-
- Found = FALSE;
-
- for (tptr = termtab; tptr; tptr = tptr->nxt) {
- if (tptr->qt_etype != ET_OLDSTYLE && tptr->qt_etype != etype)
- continue;
-
- /*
- * If a primary termtab entry was given, and it's real term name
- * does not match the secondary's generic name, continue on.
- */
- if (primett && strcmp(primett->qt_ntermname, tptr->qt_termname))
- continue;
-
- if (QueryStr)
- querystr = QueryStr;
- else
- querystr = tptr->qt_sendstr;
-
- /*
- * If this is our first time or the sendstr is the same as
- * last time, don't send it again.
- */
- if (AlwaysSend || lastt == NULL ||
- strcmp(querystr, lastquerystr) != 0) {
-
- if (WatchChars)
- (void) printf("\tSend: %s\r\n", Decode(querystr));
-
- (void) fflush(stdin);
- (void) fprintf(stderr, "%s", querystr);
- (void) fflush(stderr);
-
- lastt = tptr;
- lastquerystr = querystr;
- recvstr = InputListen(tptr);
-
- if (WatchChars)
- (void) printf("\tRead: %s\t\t[ length = %d ]\r\n",
- Decode(recvstr),
- (recvstr) ? strlen(recvstr) : 0);
- }
-
- if (tp = FindTermTab(recvstr, primett)) {
- /*
- * We found an entry that matched.
- *
- * If the user wants the real terminal name and we
- * just did all the primary names, look for a secondary
- * name.
- */
- if (tp->qt_etype != ET_OLDSTYLE && etype == ET_PRIMARY &&
- ShowReal && strcmp(tp->qt_ntermname, "-")) {
-
- dprintf("[ Look for real name of '%s' ]\n", tp->qt_termname);
- ProcTable(termtab, tp, ET_SECONDARY);
- dprintf("[ Cannot find real name of '%s' ]\n",
- tp->qt_termname);
- }
-
- PrInfo(tp, recvstr);
- Done(0);
- /*NOTREACHED*/
- }
-
- lastt = tptr;
- }
- }
-
- /*
- * Process common send sequences
- */
- void ProcCommon(termtab)
- struct termtable *termtab;
- {
- register struct termtable *tp;
- register char **cpp;
- char *recvstr;
-
- dprintf("[ Proccess Common Sequences ]\n");
-
- for (cpp = CommonSeqs; cpp && *cpp; ++cpp) {
- if (WatchChars)
- (void) printf("Trying %s\r\n", Decode(*cpp));
-
- (void) fflush(stdin);
- (void) fprintf(stderr, "%s", FixCntrl(*cpp));
- (void) fflush(stderr);
-
- recvstr = InputListen((struct termtable *)NULL);
-
- if (WatchChars && recvstr)
- (void) printf("Recieved %s\r\n", Decode(recvstr));
-
- if (recvstr) {
- if (tp = FindTermTab(recvstr, (struct termtab *)NULL))
- PrInfo(tp, recvstr);
- else
- NotRecognized();
- Done(0);
- /*NOTREACHED*/
- }
- }
-
- NotRecognized();
- Done(2);
- /*NOTREACHED*/
- }
-
- /*
- * Match a character. Each element of "list" is compared to
- * character "c". If it matches, return TRUE. If no matches
- * are found, return FALSE.
- */
- static int match_char(list, c)
- char *list;
- int c;
- {
- register char *s;
-
- for (s = list; s && *s; ++s) {
- if (*s == (char) c)
- return(TRUE);
- }
-
- return(FALSE);
- }
-
- /*
- * Parse a "string" seperated by "sep" into a null terminate
- * "array". The array "sep" should be a null terminated list
- * of characters that specify a seperator between arguments.
- * Returns the number of arguments placed into "array".
- */
- int StrToArgs(string, array, sep)
- char *string;
- char ***array;
- char *sep;
- {
- register int argc, x;
- register char *p;
- char *start, *end;
- char **argv;
-
- if (!string || !array || !sep)
- return(-1);
-
- /*
- * Count arguments
- */
- for (argc = 0, p = string; p && *p; ++argc) {
- while (p && *p && match_char(sep, *p)) /* skip leading seperators */
- ++p;
-
- while (p && *p && !match_char(sep, *p)) /* skip over argument */
- ++p;
- }
-
- /*
- * If we've allocated memory before for this buffer, free it now.
- */
- argv = *array;
- if (argv != NULL) {
- for (x = 0; argv[x] != NULL; ++x)
- (void) free(argv[x]);
- (void) free(argv);
- argv = NULL;
- }
-
- /*
- * Allocate enough memory for each argument + NULL terminator
- */
- argv = (char **) xmalloc((argc + 1) * sizeof(char *));
-
- /*
- * Actually parse the string and place arguments in buffer.
- */
- for (x = 0, p = string; x < argc; ++x) {
- while (p && *p && match_char(sep, *p)) /* skip leading seperators */
- ++p;
-
- start = p; /* mark start of argument */
- while (p && *p && !match_char(sep, *p)) /* skip over argument */
- ++p;
- end = p; /* mark end of argument */
-
- /* Save argument */
- argv[x] = (char *) xmalloc((end-start)+1);
- strncpy(argv[x], start, end-start);
- argv[x][end-start] = (char) NULL;
- }
- argv[argc] = NULL;
- *array = argv;
-
- return(argc);
- }
-
- /*
- * Initialize things.
- */
- Init(argc, argv)
- int argc;
- char **argv;
- {
- static char **av = NULL;
- static char **tav = NULL;
- register int x;
- char *envstr;
- int ac;
-
- /*
- * Get options from $QTERMOPTIONS
- */
- if ((envstr = getenv("QTERMOPTIONS")) != NULL)
- if ((ac = StrToArgs(envstr, &av, " \t")) > 0) {
- tav = (char **) xmalloc((sizeof(char *) * ac) + 2);
- tav[0] = argv[0];
- for (x = 0; x < ac; ++x)
- tav[x+1] = av[x];
- Config(ac+1, tav);
- }
-
- /*
- * Now try command line options
- */
- Config(argc, argv);
-
- if (Debug)
- ++WatchChars;
- }
-
- /*
- * Print version info
- */
- void PrintVersion()
- {
- printf("Qterm Version %s", VERSION);
- if (PATCHLEVEL)
- printf(".%d", PATCHLEVEL);
- printf(" - Release %s, Patch level %d\n", VERSION, PATCHLEVEL);
- }
-
- /*
- * Where it all begins
- */
- main(argc, argv)
- int argc;
- char **argv;
- {
- Init(argc, argv);
-
- if (PrVersion) {
- PrintVersion();
- exit(0);
- }
-
- SetModes();
- MakeTable();
- if (TryCommon)
- ProcCommon(TermTable);
- else
- ProcTable(TermTable, (struct termtable *)NULL, ET_PRIMARY);
- ReSetModes();
-
- if (!Found)
- NotRecognized();
-
- exit((Found) ? 0 : 2);
- }
-